#include <iostream>
#include <vector>

#include <SDL3/SDL.h>
#include <SDL3_image/SDL_image.h>

// 全局常量
const int MOVE_SPEED = 2;
const int BOX_SPEED = 1;
const int GROUND_HEIGHT = 400;
const int SCREEN_WIDTH = 800;
const int SCREEN_HEIGHT = 600;
const int FPS = 120;
const Uint64 FRAME_DELAY = 1000 / FPS;
const float GRAVITY = 0.1f;
const float JUMP_STRENGTH = 5.0f;
const int PLAYER_WIDTH = 10;
const int PLAYER_HEIGHT = 22;
const int BOX_WIDTH = 22;
const int BOX_HEIGHT = 22;
const float DISTANCE_MIN = 5;

// 游戏对象基类
class GameObject
{
public:
    float realX, realY;
    float nextX, nextY;
    float velocityY = 0, velocityX = 0;
    int width, height;

    bool isMovable = false;

    bool isJumping = false;

    GameObject(int x, int y, int width, int height, bool isMovable);

    virtual void render(SDL_Renderer* renderer) = 0;

    virtual void beforeHandleCollision(const std::vector<GameObject*>& platforms);
    virtual void moveAndHandleCollision(const std::vector<GameObject*>& platforms);
    virtual void afterHandleCollision(const std::vector<GameObject*>& platforms);

    virtual void handleInput(const bool* keystates);
    virtual void beforeUpdate(const bool* keystates, const std::vector<GameObject*>& platforms);
    virtual void update(const bool* keystates, const std::vector<GameObject*>& platforms);
    virtual void afterUpdate(const bool* keystates, const std::vector<GameObject*>& platforms);

    virtual void jump();

    SDL_FRect getRealRect() const;
    SDL_FRect getNextRect() const;
};

// 箱子
class BoxObject : public GameObject
{
public:
    SDL_Texture* texture;

    BoxObject(int x, int y, int width, int height, SDL_Texture*& texture);

    void render(SDL_Renderer* renderer);
};

// 枚举类
enum class PlayerState
{
    Standing,
    WalkingLeft,
    WalkingRight,
};

enum class ObjectTexture
{
    Standing,
    Left,
    Right,
    WalkLeft1,
    WalkLeft2,
    WalkRight1,
    WalkRight2,
    PlayerNum,
    Box = PlayerNum,
    Ground,
    Background,
    Num
};

// 状态机类
class StateMachine
{
public:
    PlayerState currentState = PlayerState::Standing;
    int frameCounter = 0;
    int walkFrame = 0;
    size_t currentTextureIndex = 0;

    void update(PlayerState state);
    size_t getCurrentTextureIndex() const;
};

struct KeyMap
{
    unsigned char key_left;
    unsigned char key_right;
    unsigned char key_jump;
};


// 玩家
class PlayerObject : public GameObject
{
public:
    std::vector<SDL_Texture*> textures;
    StateMachine stateMachine;
    KeyMap keyMap;

public:
    PlayerObject(int x, int y, int width, int height, std::vector<SDL_Texture*>& textures, KeyMap keyMap);

    virtual void render(SDL_Renderer* renderer);

    virtual void handleInput(const bool* keystates);
};

static bool checkCollision(const SDL_FRect& a, const SDL_FRect& b);
static bool checkStanding(const SDL_FRect& a, const SDL_FRect& b);
static float distance(const SDL_FRect& a, const SDL_FRect& b);

GameObject::GameObject(int x, int y, int width, int height, bool isMovable) : realX(static_cast<float>(x)), realY(static_cast<float>(y)),
nextX(realX), nextY(realY),
width(width), height(height),
isMovable(isMovable)
{
}

void GameObject::beforeHandleCollision(const std::vector<GameObject*>& platforms)
{
    nextX = realX + velocityX;
    nextY = realY - velocityY;
}

void GameObject::moveAndHandleCollision(const std::vector<GameObject*>& platforms)
{
    bool onPlatform = false;
    for (const auto& obj : platforms)
    {
        if (obj == this) // 避免与自身发生碰撞
        {
            continue;
        }

        if (checkCollision(getNextRect(), obj->getRealRect())) // 撞别人
        {
            // 水平碰撞检测
            // 向右
            if (velocityX > 0)
            {
                nextX = realX;
                if (nextX < obj->realX)
                {
                    // 靠太近了，被往回顶
                    if (distance(getNextRect(), obj->getRealRect()) <= DISTANCE_MIN)
                    {
                        nextX = obj->realX - width;
                    }
                }
                else
                {
                    nextX = obj->realX + obj->width;
                    continue;
                }
                if (obj->isMovable)
                {
                    obj->velocityX = BOX_SPEED;
                }
                velocityX = -velocityX;
            }
            // 向左
            if (velocityX < 0)
            {
                nextX = realX;
                if (nextX > obj->realX)
                {
                    // 靠太近了，被往回顶
                    if (distance(getNextRect(), obj->getRealRect()) <= DISTANCE_MIN)
                    {
                        nextX = obj->realX + obj->width;
                    }
                }
                else
                {
                    nextX = obj->realX - width;
                    continue;
                }
                if (obj->isMovable)
                {
                    obj->velocityX = -BOX_SPEED;
                }
                velocityX = -velocityX;
            }

            // 垂直碰撞检测
            if (velocityY > 0)
            {
                nextY = realY;
                if (nextY < obj->realY)
                {
                    if (distance(getNextRect(), obj->getRealRect()) <= DISTANCE_MIN)
                    {
                        nextY = obj->realY - height;
                    }
                    velocityY = 0;
                    onPlatform = true;
                }
                else
                {
                    nextY = obj->realY + obj->height;
                    velocityY = 0;
                    onPlatform = true;
                    continue;
                }
            }
            if (velocityY < 0)
            {
                nextY = realY;
                if (nextY > obj->realY)
                {
                    if (distance(getNextRect(), obj->getRealRect()) <= DISTANCE_MIN)
                    {
                        nextY = obj->realY + obj->height;
                    }
                    velocityY = 0;
                    onPlatform = true;
                }
                else
                {
                    nextY = obj->realY - height;
                    velocityY = 0;
                    onPlatform = true;
                    continue;
                }
            }
        }

        // 站在物体上检测
        if (checkStanding(getNextRect(), obj->getRealRect()) &&
            !checkCollision(getNextRect(), obj->getRealRect()))
        {
            nextY = obj->realY - height;
            velocityY = 0;
            onPlatform = true;
        }
    }

    // 屏幕边界检测
    // 左边界检测
    if (nextX < 0)
    {
        nextX = 0;
        velocityX = -velocityX;
    }
    // 右边界检测
    if (nextX > SCREEN_WIDTH - width)
    {
        nextX = SCREEN_WIDTH - width;
        velocityX = -velocityX;
    }

    // 触地检测
    bool onGround = false;
    float groundLevel = static_cast<float>(GROUND_HEIGHT - height);
    if (nextY >= groundLevel)
    {
        nextY = groundLevel;
        velocityY = 0;
        onGround = true;
    }

    if (onPlatform ||
        onGround)
    {
        isJumping = false;
    }
    else
    {
        isJumping = true;
    }
}

void GameObject::afterHandleCollision(const std::vector<GameObject*>& platforms)
{
    realX = nextX;
    realY = nextY;

    if (isJumping)
    {
        velocityY -= GRAVITY;
    }
}

void GameObject::handleInput(const bool* keystates)
{}

void GameObject::beforeUpdate(const bool* keystates, const std::vector<GameObject*>& platforms)
{
    beforeHandleCollision(platforms);
}

void GameObject::update(const bool* keystates, const std::vector<GameObject*>& platforms)
{
    moveAndHandleCollision(platforms);
}

void GameObject::afterUpdate(const bool* keystates, const std::vector<GameObject*>& platforms)
{
    afterHandleCollision(platforms);
}

void GameObject::jump()
{
    if (!isJumping)
    {
        isJumping = true;
        velocityY = JUMP_STRENGTH;
    }
}

SDL_FRect GameObject::getRealRect() const
{
    return { realX, realY, static_cast<float>(width), static_cast<float>(height) };
}

SDL_FRect GameObject::getNextRect() const
{
    return { nextX, nextY, static_cast<float>(width), static_cast<float>(height) };
}

// 简单的碰撞检测
bool checkCollision(const SDL_FRect& a, const SDL_FRect& b)
{
    // 计算矩形A的边
    float leftA = a.x;
    float rightA = a.x + a.w;
    float topA = a.y;
    float bottomA = a.y + a.h;

    // 计算矩形B的边
    float leftB = b.x;
    float rightB = b.x + b.w;
    float topB = b.y;
    float bottomB = b.y + b.h;

    // 检查是否有分离轴
    if (bottomA <= topB)
        return false; // A在B的上方
    if (topA >= bottomB)
        return false; // A在B的下方
    if (rightA <= leftB)
        return false; // A在B的左侧
    if (leftA >= rightB)
        return false; // A在B的右侧

    // 如果以上条件都不满足，则发生碰撞
    return true;
}

// 站在物体上检测
bool checkStanding(const SDL_FRect& a, const SDL_FRect& b)
{
    // 计算矩形A的边
    float leftA = a.x;
    float rightA = a.x + a.w;
    float topA = a.y;
    float bottomA = a.y + a.h;

    // 计算矩形B的边
    float leftB = b.x;
    float rightB = b.x + b.w;
    float topB = b.y;
    float bottomB = b.y + b.h;

    // 检查是否有分离轴
    if (bottomA < topB)
        return false; // A在B的上方，且非接触
    if (topA >= bottomB)
        return false; // A在B的下方
    if (rightA <= leftB)
        return false; // A在B的左侧
    if (leftA >= rightB)
        return false; // A在B的右侧

    // 如果以上条件都不满足，则发生碰撞
    return true;
}

float distance(const SDL_FRect& a, const SDL_FRect& b)
{
    // 计算矩形 a 的中心点
    float a_center_x = a.x + a.w / 2.0f;
    float a_center_y = a.y + a.h / 2.0f;

    // 计算矩形 b 的中心点
    float b_center_x = b.x + b.w / 2.0f;
    float b_center_y = b.y + b.h / 2.0f;

    // 计算两个中心点之间的距离
    float dx = a_center_x - b_center_x;
    float dy = a_center_y - b_center_y;
    return sqrtf(dx * dx + dy * dy);
}

BoxObject::BoxObject(int x, int y, int width, int height, SDL_Texture*& texture)
    : texture(texture), GameObject(x, y, width, height, true) {}

void BoxObject::render(SDL_Renderer* renderer)
{
    SDL_FRect rect = { realX, realY, static_cast<float>(width), static_cast<float>(height) };
    SDL_RenderTexture(renderer, texture, nullptr, &rect);
}

void StateMachine::update(PlayerState state)
{
    frameCounter++;

    if (state == PlayerState::WalkingLeft)
    {
        if (!(currentState == PlayerState::WalkingLeft))
        {
            currentState = state;
            frameCounter = 0;
        }
        else if (frameCounter >= 12)
        {
            walkFrame = (walkFrame + 1) % 2;
            frameCounter = 0;
        }
    }
    else if (state == PlayerState::WalkingRight)
    {
        if (!(currentState == PlayerState::WalkingRight))
        {
            currentState = state;
            frameCounter = 0;
        }
        else if (frameCounter >= 12)
        {
            walkFrame = (walkFrame + 1) % 2;
            frameCounter = 0;
        }
    }
    else
    {
        currentState = PlayerState::Standing;
        walkFrame = 0;
    }

    switch (currentState)
    {
    case PlayerState::Standing:
        currentTextureIndex = (size_t)ObjectTexture::Standing;
        break;
    case PlayerState::WalkingLeft:
        currentTextureIndex = (size_t)ObjectTexture::WalkLeft1 + walkFrame;
        break;
    case PlayerState::WalkingRight:
        currentTextureIndex = (size_t)ObjectTexture::WalkRight1 + walkFrame;
        break;
    default:
        currentTextureIndex = (size_t)ObjectTexture::Standing;
        break;
    }
}

size_t StateMachine::getCurrentTextureIndex() const { return currentTextureIndex; }


PlayerObject::PlayerObject(int x, int y, int width, int height, std::vector<SDL_Texture*>& textures, KeyMap keyMap)
    : textures(textures), keyMap(keyMap), GameObject(x, y, width, height, false) {}

void PlayerObject::render(SDL_Renderer* renderer)
{
    SDL_FRect rect = { realX, realY, static_cast<float>(width), static_cast<float>(height) };
    SDL_RenderTexture(renderer, textures[stateMachine.getCurrentTextureIndex()], nullptr, &rect);
}

void PlayerObject::handleInput(const bool* keystates)
{
    velocityX = 0;
    PlayerState state = PlayerState::Standing;
    if (keystates[keyMap.key_left])
    {
        velocityX = -MOVE_SPEED;
        state = PlayerState::WalkingLeft;
    }
    if (keystates[keyMap.key_right])
    {
        velocityX = MOVE_SPEED;
        state = PlayerState::WalkingRight;
    }
    if (keystates[keyMap.key_jump])
    {
        jump();
    }
    stateMachine.update(state);
}

// 加载纹理
SDL_Texture* loadTexture(const std::string& path, SDL_Renderer* renderer)
{
    SDL_Texture* texture = IMG_LoadTexture(renderer, path.c_str());
    if (!texture)
    {
        std::cerr << "Failed to load texture: " << path << ". SDL_Error: " << SDL_GetError() << std::endl;
    }
    return texture;
}

// 清理资源
void cleanup(SDL_Window*& window, SDL_Renderer*& renderer, std::vector<SDL_Texture*>& textures)
{
    for (auto* texture : textures)
    {
        SDL_DestroyTexture(texture);
    }
    SDL_DestroyRenderer(renderer);
    SDL_DestroyWindow(window);
    SDL_Quit();
}

// 初始化
bool init(SDL_Window*& window, SDL_Renderer*& renderer, std::vector<SDL_Texture*>& textures)
{
    if (!SDL_Init(SDL_INIT_VIDEO))
    {
        std::cerr << "SDL could not initialize! SDL_Error: " << SDL_GetError() << std::endl;
        return false;
    }

    window = SDL_CreateWindow("Simple Game Engine", SCREEN_WIDTH, SCREEN_HEIGHT, 0);
    if (!window)
    {
        std::cerr << "Window could not be created! SDL_Error: " << SDL_GetError() << std::endl;
        SDL_Quit();
        return false;
    }

    renderer = SDL_CreateRenderer(window, nullptr);
    if (!renderer)
    {
        std::cerr << "Renderer could not be created! SDL_Error: " << SDL_GetError() << std::endl;
        SDL_DestroyWindow(window);
        SDL_Quit();
        return false;
    }

    // 加载纹理
    textures.resize(static_cast<size_t>(ObjectTexture::Num));
    textures[static_cast<size_t>(ObjectTexture::Standing)] = loadTexture("assets/player_standing.png", renderer);
    textures[static_cast<size_t>(ObjectTexture::Left)] = loadTexture("assets/player_left.png", renderer);
    textures[static_cast<size_t>(ObjectTexture::Right)] = loadTexture("assets/player_right.png", renderer);
    textures[static_cast<size_t>(ObjectTexture::WalkLeft1)] = loadTexture("assets/player_walk_left_1.png", renderer);
    textures[static_cast<size_t>(ObjectTexture::WalkLeft2)] = loadTexture("assets/player_walk_left_2.png", renderer);
    textures[static_cast<size_t>(ObjectTexture::WalkRight1)] = loadTexture("assets/player_walk_right_1.png", renderer);
    textures[static_cast<size_t>(ObjectTexture::WalkRight2)] = loadTexture("assets/player_walk_right_2.png", renderer);
    textures[static_cast<size_t>(ObjectTexture::Box)] = loadTexture("assets/box.png", renderer);
    textures[static_cast<size_t>(ObjectTexture::Ground)] = loadTexture("assets/ground.png", renderer);
    textures[static_cast<size_t>(ObjectTexture::Background)] = loadTexture("assets/background.png", renderer);

    // 检查纹理是否加载成功
    for (const auto& texture : textures)
    {
        if (!texture)
        {
            cleanup(window, renderer, textures);
            return false;
        }
    }

    return true;
}

// 主函数
int main()
{
    std::vector<SDL_Texture*> textures;
    SDL_Window* window = nullptr;
    SDL_Renderer* renderer = nullptr;

    if (!init(window, renderer, textures))
    {
        std::cerr << "Initialization failed. Exiting..." << std::endl;
        return 1;
    }

    // 创建玩家和箱子
    PlayerObject player1(200, GROUND_HEIGHT - PLAYER_HEIGHT - 50, PLAYER_WIDTH, PLAYER_HEIGHT, textures, { SDL_SCANCODE_LEFT, SDL_SCANCODE_RIGHT, SDL_SCANCODE_UP });
    PlayerObject player2(500, GROUND_HEIGHT - PLAYER_HEIGHT, PLAYER_WIDTH, PLAYER_HEIGHT, textures, { SDL_SCANCODE_A, SDL_SCANCODE_D, SDL_SCANCODE_W });
    BoxObject box1(200, GROUND_HEIGHT - BOX_HEIGHT, BOX_WIDTH, BOX_HEIGHT, textures[static_cast<size_t>(ObjectTexture::Box)]);
    BoxObject box2(300, GROUND_HEIGHT - BOX_HEIGHT, BOX_WIDTH, BOX_HEIGHT, textures[static_cast<size_t>(ObjectTexture::Box)]);
    BoxObject box3(400, GROUND_HEIGHT - BOX_HEIGHT, BOX_WIDTH, BOX_HEIGHT, textures[static_cast<size_t>(ObjectTexture::Box)]);
    std::vector<GameObject*> platforms;
    platforms.push_back(&player1);
    platforms.push_back(&player2);
    platforms.push_back(&box1);
    platforms.push_back(&box2);
    platforms.push_back(&box3);

    bool running = true;
    SDL_Event e;
    Uint64 startFrameTime, frameTime;

    while (running)
    {
        startFrameTime = SDL_GetTicks();

        while (SDL_PollEvent(&e))
        {
            if (e.type == SDL_EVENT_QUIT)
            {
                running = false;
            }
        }

        const bool* keystates = SDL_GetKeyboardState(nullptr);
        for (auto obj : platforms)
        {
            obj->handleInput(keystates);
        }
        for (auto obj : platforms)
        {
            obj->beforeUpdate(keystates, platforms);
        }
        for (auto obj : platforms)
        {
            obj->update(keystates, platforms);
        }
        for (auto obj : platforms)
        {
            obj->afterUpdate(keystates, platforms);
        }

        // 渲染背景
        SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
        SDL_RenderClear(renderer);

        // 绘制背景纹理
        SDL_FRect groundRect = { 0, 0, static_cast<float>(SCREEN_WIDTH), static_cast<float>(GROUND_HEIGHT) };
        SDL_RenderTexture(renderer, textures[static_cast<size_t>(ObjectTexture::Ground)], nullptr, &groundRect);

        // 绘制地面纹理
        SDL_FRect backgroundRect = { 0, static_cast<float>(GROUND_HEIGHT), static_cast<float>(SCREEN_WIDTH), static_cast<float>(SCREEN_HEIGHT - GROUND_HEIGHT) };
        SDL_RenderTexture(renderer, textures[static_cast<size_t>(ObjectTexture::Background)], nullptr, &backgroundRect);

        // 绘制玩家和箱子
        for (auto obj : platforms)
        {
            obj->render(renderer);
        }

        SDL_RenderPresent(renderer);

        frameTime = SDL_GetTicks() - startFrameTime;
        if (frameTime < FRAME_DELAY)
        {
            SDL_Delay(static_cast<Uint32>(FRAME_DELAY - frameTime));
        }
    }

    // 清理资源
    cleanup(window, renderer, textures);

    return 0;
}